package chains

import (
	"context"
	"fmt"
	"path/filepath"
	"strings"
	"time"
	"xagent/internal/memory"

	"github.com/google/uuid"
)

// CodeAnalysisChain analyzes code using multiple tools
type CodeAnalysisChain struct {
	*BaseChain
	workdir string
}

// NewCodeAnalysisChain creates a new code analysis chain
func NewCodeAnalysisChain(base *BaseChain, workdir string) *CodeAnalysisChain {
	return &CodeAnalysisChain{
		BaseChain: base,
		workdir:   workdir,
	}
}

// CodeAnalysisInput represents the input for code analysis
type CodeAnalysisInput struct {
	Path        string   // path to analyze
	Languages   []string // programming languages to focus on
	MaxFiles    int      // maximum number of files to analyze
	IncludeTest bool     // whether to include test files
}

// CodeAnalysisResult represents the result of code analysis
type CodeAnalysisResult struct {
	Files        []FileAnalysis    // analysis of individual files
	Dependencies map[string]string // project dependencies
	Metrics      map[string]int    // code metrics
	Issues       []Issue           // potential issues found
}

// FileAnalysis represents analysis of a single file
type FileAnalysis struct {
	Path      string
	Language  string
	LOC       int
	Imports   []string
	Classes   []string
	Functions []string
	Issues    []Issue
}

// Issue represents a potential code issue
type Issue struct {
	Type        string
	Severity    string
	File        string
	Line        int
	Description string
}

// Execute implements Chain
func (c *CodeAnalysisChain) Execute(ctx context.Context, input interface{}) (interface{}, error) {
	analysisInput, ok := input.(CodeAnalysisInput)
	if !ok {
		return nil, fmt.Errorf("invalid input type")
	}

	// List files to analyze
	files, err := c.listFiles(ctx, analysisInput)
	if err != nil {
		return nil, fmt.Errorf("failed to list files: %v", err)
	}

	// Analyze each file
	result := &CodeAnalysisResult{
		Files:        make([]FileAnalysis, 0),
		Dependencies: make(map[string]string),
		Metrics:      make(map[string]int),
		Issues:       make([]Issue, 0),
	}

	for _, file := range files {
		analysis, err := c.analyzeFile(ctx, file)
		if err != nil {
			return nil, fmt.Errorf("failed to analyze file %s: %v", file, err)
		}
		result.Files = append(result.Files, *analysis)
	}

	// Aggregate metrics
	c.aggregateMetrics(result)

	// Store results in memory
	if err := c.storeResults(ctx, result); err != nil {
		return nil, fmt.Errorf("failed to store results: %v", err)
	}

	return result, nil
}

func (c *CodeAnalysisChain) listFiles(ctx context.Context, input CodeAnalysisInput) ([]string, error) {
	// Use file tool to list files
	params := map[string]interface{}{
		"operation": "list",
		"path":      input.Path,
	}

	result, err := c.tools.ExecuteTool(ctx, "file", params)
	if err != nil {
		return nil, err
	}

	if !result.Success {
		return nil, fmt.Errorf("file tool failed: %s", result.Error)
	}

	files, ok := result.Data.([]string)
	if !ok {
		return nil, fmt.Errorf("unexpected result type from file tool")
	}

	// Filter files based on input criteria
	var filtered []string
	for _, file := range files {
		if len(filtered) >= input.MaxFiles {
			break
		}

		ext := filepath.Ext(file)
		if !input.IncludeTest && isTestFile(file) {
			continue
		}

		if len(input.Languages) > 0 && !isLanguageMatch(ext, input.Languages) {
			continue
		}

		filtered = append(filtered, file)
	}

	return filtered, nil
}

func (c *CodeAnalysisChain) analyzeFile(ctx context.Context, path string) (*FileAnalysis, error) {
	// Read file content
	params := map[string]interface{}{
		"operation": "read",
		"path":      path,
	}

	result, err := c.tools.ExecuteTool(ctx, "file", params)
	if err != nil {
		return nil, err
	}

	if !result.Success {
		return nil, fmt.Errorf("file tool failed: %s", result.Error)
	}

	content, ok := result.Data.(string)
	if !ok {
		return nil, fmt.Errorf("unexpected result type from file tool")
	}

	// Create basic analysis
	analysis := &FileAnalysis{
		Path:      path,
		Language:  detectLanguage(path),
		LOC:       countLines(content),
		Imports:   extractImports(content, detectLanguage(path)),
		Classes:   extractClasses(content, detectLanguage(path)),
		Functions: extractFunctions(content, detectLanguage(path)),
		Issues:    make([]Issue, 0),
	}

	// Store file analysis in memory
	if err := c.storeFileAnalysis(ctx, analysis); err != nil {
		return nil, err
	}

	return analysis, nil
}

func (c *CodeAnalysisChain) aggregateMetrics(result *CodeAnalysisResult) {
	totalLOC := 0
	langCount := make(map[string]int)
	issueCount := make(map[string]int)

	for _, file := range result.Files {
		totalLOC += file.LOC
		langCount[file.Language]++

		for _, issue := range file.Issues {
			issueCount[issue.Type]++
		}
	}

	result.Metrics["total_loc"] = totalLOC
	result.Metrics["total_files"] = len(result.Files)
	for lang, count := range langCount {
		result.Metrics[fmt.Sprintf("files_%s", lang)] = count
	}
	for issueType, count := range issueCount {
		result.Metrics[fmt.Sprintf("issues_%s", issueType)] = count
	}
}

func (c *CodeAnalysisChain) storeResults(ctx context.Context, result *CodeAnalysisResult) error {
	memory := &memory.MemoryEntry{
		ID:      uuid.NewString(),
		Type:    memory.MemoryTypeCode,
		Content: fmt.Sprintf("Code analysis results for %d files", len(result.Files)),
		Metadata: map[string]interface{}{
			"total_files": len(result.Files),
			"metrics":     result.Metrics,
		},
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}

	return c.memory.Store(ctx, memory)
}

func (c *CodeAnalysisChain) storeFileAnalysis(ctx context.Context, analysis *FileAnalysis) error {
	memory := &memory.MemoryEntry{
		ID:      uuid.NewString(),
		Type:    memory.MemoryTypeCode,
		Content: fmt.Sprintf("Analysis for file: %s", analysis.Path),
		Metadata: map[string]interface{}{
			"path":      analysis.Path,
			"language":  analysis.Language,
			"loc":       analysis.LOC,
			"imports":   analysis.Imports,
			"classes":   analysis.Classes,
			"functions": analysis.Functions,
		},
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}

	return c.memory.Store(ctx, memory)
}

// Helper functions

func isTestFile(path string) bool {
	name := filepath.Base(path)
	return strings.Contains(strings.ToLower(name), "test")
}

func isLanguageMatch(ext string, languages []string) bool {
	ext = strings.TrimPrefix(ext, ".")
	for _, lang := range languages {
		if strings.EqualFold(ext, lang) {
			return true
		}
	}
	return false
}

func detectLanguage(path string) string {
	ext := filepath.Ext(path)
	switch strings.ToLower(ext) {
	case ".go":
		return "go"
	case ".py":
		return "python"
	case ".js":
		return "javascript"
	case ".ts":
		return "typescript"
	case ".java":
		return "java"
	default:
		return "unknown"
	}
}

func countLines(content string) int {
	return len(strings.Split(content, "\n"))
}

func extractImports(content, language string) []string {
	// This is a simple implementation
	// In a real system, you would want to use proper parsers for each language
	var imports []string
	lines := strings.Split(content, "\n")

	for _, line := range lines {
		line = strings.TrimSpace(line)
		switch language {
		case "go":
			if strings.HasPrefix(line, "import ") {
				imports = append(imports, strings.Trim(line[7:], "\""))
			}
		case "python":
			if strings.HasPrefix(line, "import ") || strings.HasPrefix(line, "from ") {
				imports = append(imports, line)
			}
		}
	}

	return imports
}

func extractClasses(content, language string) []string {
	// Simplified implementation
	var classes []string
	lines := strings.Split(content, "\n")

	for _, line := range lines {
		line = strings.TrimSpace(line)
		switch language {
		case "go":
			if strings.HasPrefix(line, "type ") && strings.Contains(line, "struct") {
				parts := strings.Fields(line)
				if len(parts) > 1 {
					classes = append(classes, parts[1])
				}
			}
		case "python":
			if strings.HasPrefix(line, "class ") {
				parts := strings.Fields(line)
				if len(parts) > 1 {
					classes = append(classes, parts[1])
				}
			}
		}
	}

	return classes
}

func extractFunctions(content, language string) []string {
	// Simplified implementation
	var functions []string
	lines := strings.Split(content, "\n")

	for _, line := range lines {
		line = strings.TrimSpace(line)
		switch language {
		case "go":
			if strings.HasPrefix(line, "func ") {
				parts := strings.Fields(line)
				if len(parts) > 1 {
					functions = append(functions, parts[1])
				}
			}
		case "python":
			if strings.HasPrefix(line, "def ") {
				parts := strings.Fields(line)
				if len(parts) > 1 {
					functions = append(functions, parts[1])
				}
			}
		}
	}

	return functions
}
